#coding=utf-8

require "aio/core"

class Aio::Module::InputStyle::Console < Aio::Module::InputStyle
  include Aio::Module
  include Aio::Ui::Verbose

  class Machine

    attr_accessor :regs, :state

    def initialize
      @cmd_state = CmdState.new(self)
      @context_state = ContextState.new(self)
      @empty_state = EmptyState.new(self)
      @cmd_context = CmdContext.new

      @state = @empty_state
    end

    def state
      @state.class
    end

    def cmd_context
      @cmd_context
    end

    def to_cmd_state
      @state = @cmd_state
    end

    def to_context_state
      @state = @context_state
    end

    def to_empty_state
      @state = @empty_state
    end

    def parse_line(line)
      @state.parse_line(line)
    end
  end

  class CmdContext

    attr_accessor :device_name, :cmd, :context

    # all_info = [[device_name, cmd, context] ... ]
    attr_accessor :all_info

    def initialize
      @device_name = ""
      @cmd = ""
      @context = []
      @all_info = []
    end

    def done
      # 当之前有cmd内容的时候,比较两个的长度
      # 长的保留
      info_idx = has_cmd?
      if info_idx
        if @all_info[info_idx][2].size < context.size
          @all_info[info_idx][2] = context
        end
      else

        # 如果都没有,那么就直接添加
        @all_info << [device_name, cmd, context]
      end
      self.clear
    end

    def has_cmd?
      @all_info.each_with_index do |info, idx|
        next unless info[0] == device_name
        return idx if info[1] == cmd
      end

      return nil
    end

    def clear
      @device_name = ""
      @cmd = ""
      @context = []
    end
  end

  class State

    include Aio::Ui::Verbose

    def initialize(machine)
      @machine = machine
    end

    def cmd_context
      @machine.cmd_context
    end
  end

  class EmptyState < State
    def parse_line(line)

      # 当前是空的状态
      # 判断当前行有没有模块中的命令
      line = Aio::Base::Toolkit::String.safe(line)
      return if line.include?('Command is')
      return unless @machine.regs.match(line)

      @machine.to_cmd_state
      @machine.parse_line(line)
    end
  end

  class CmdState < State
    def parse_line(line)
      # 判断是不是cmd命令行
      res = /^[\[|<]?(?<name>[^\s]*)(#|>|\])(?<cmd>.*)/.match(line)
      if res.nil?
        @machine.to_empty_state
        return
      end

      # 当已经发现了是cmd行,那么紧接下来就是收集文本内容
      cmd_context.device_name = res[:name]
      cmd_context.cmd = res[:cmd].strip
      @machine.to_context_state
    end
  end

  class ContextState < State

    def parse_line(line)
      # 如果这行是命令行格式,那么就代表上一个cmd内容结束
      # 如果这里出问题，关注 CmdState
      tmp_reg = ::Regexp.new "^[\\[|<]?#{cmd_context.device_name}[#|>|\\]]{1}"


      # 可能会出现UTF-8识别不出摄氏度符号的问题
      # 已经通过安全字符解决
      begin
        if tmp_reg.match(line)
          cmd_context.done

          # 完成后将状态设置为空,并且重新分析这一行
          # 毕竟如果这行也cmd行呢
          @machine.to_empty_state
          return @machine.parse_line(line)
        end
      rescue => e
        print_error cmd_context.device_name + " 设备巡检信息编码出现问题"
        print_error line + " : " + e.message
      end


      cmd_context.context << line
    end
  end

  def initialize
    super({
      :author				=> "Elin",
      :description 	=> "这个模块用于对命令行格式的输入处理",
      :platform 		=> "all",
    })
    @machine = Machine.new
  end

  def parse
    dir = self.input_file
    dir_pn = Pathname.new(dir)

    # 加载所有的cmd正则表达式
    @machine.regs = merge_regs(ext_info[:cmds_reg])

    # 如果是单个文件的话,直接进入文件解析模式
    if dir_pn.file?
      parse_file(dir_pn)
    else

      # 如果是文件夹的话,那么就对每个文件进行解析
      Find.find(dir) do |file|
        fn = Pathname.new(file)
        parse_file(file) if fn.file?
      end
    end

    @machine.cmd_context.all_info.each do |info|
      yield info[0], info[1], info[2]
    end
  end

  def parse_file(file)
    fo = File.open(file, "r+", :encoding => "gbk")

    fo.each_line do |line|
      begin 
        line = Aio::Base::Toolkit::String.safe(line)
        line = line.strip
      rescue => e
        print_error line + " : " + e.message
      end

      @machine.parse_line(line)
    end

    # 如果最后没有done结束,则手动结束
    if @machine.state == ContextState
      @machine.cmd_context.done 
      @machine.to_cmd_state
    end
  end

  def merge_regs(arr_regs)
    regs = Aio::Base::Toolkit::Regexp.merge(arr_regs).to_s
    regs.gsub!('(^', '(')
    ::Regexp.new(regs)
  end
end


